#include "dev/kbd.h"
#include "cpu/irq.h"
#include "comm/cpu_instr.h"
#include "tools/log.h"
#include "tools/klib.h"
#include "dev/tty.h"

static kbd_status_t kdb_status;
// 键盘映射表
static const key_map_t key_map_table[256] = {
        [0x2] = {'1', '!'},
        [0x3] = {'2', '@'},
        [0x4] = {'3', '#'},
        [0x5] = {'4', '$'},
        [0x6] = {'5', '%'},
        [0x7] = {'6', '^'},
        [0x08] = {'7', '&'},
        [0x09] = {'8', '*' },
        [0x0A] = {'9', '('},
        [0x0B] = {'0', ')'},
        [0x0C] = {'-', '_'},
        [0x0D] = {'=', '+'},
        [0x0E] = {0x7f, 0x7f},
        [0x0F] = {'\t', '\t'},
        [0x10] = {'q', 'Q'},
        [0x11] = {'w', 'W'},
        [0x12] = {'e', 'E'},
        [0x13] = {'r', 'R'},
        [0x14] = {'t', 'T'},
        [0x15] = {'y', 'Y'},
        [0x16] = {'u', 'U'},
        [0x17] = {'i', 'I'},
        [0x18] = {'o', 'O'},
        [0x19] = {'p', 'P'},
        [0x1A] = {'[', '{'},
        [0x1B] = {']', '}'},
        [0x1C] = {'\n', '\n'},
        [0x1E] = {'a', 'A'},
        [0x1F] = {'s', 'S'},
        [0x20] = {'d',  'D'},
        [0x21] = {'f', 'F'},
        [0x22] = {'g', 'G'},
        [0x23] = {'h', 'H'},
        [0x24] = {'j', 'J'},
        [0x25] = {'k', 'K'},
        [0x26] = {'l', 'L'},
        [0x27] = {';', ':'},
        [0x28] = {'\'', '"'},
        [0x29] = {'`', '~'},
        [0x2B] = {'\\', '|'},
        [0x2C] = {'z', 'Z'},
        [0x2D] = {'x', 'X'},
        [0x2E] = {'c', 'C'},  
        [0x2F] = {'v', 'V'},
        [0x30] = {'b', 'B'},
        [0x31] = {'n', 'N'},
        [0x32] = {'m', 'M'},
        [0x33] = {',', '<'},
        [0x34] = {'.', '>'},
        [0x35] = {'/', '?'},
        [0x39] = {' ', ' '},
};




// 是否为按下键盘的输入码
// 最高位为1 代表为结束码(break code)
// 最高位为0 代表位输入码(make code)
static inline uint8_t is_make_code(uint8_t raw_code)
{
    return !(raw_code & 0x80);
}

static inline char get_key(uint8_t raw_code)
{
    return (raw_code & 0x7f);
}

// 处理f1-f8的显示器切换
static void do_fx_key(char key)
{
    int tty_idx = key - KEY_F1;
    if(kdb_status.lctrl_press || kdb_status.rctrl_press)
    {
        tty_select(tty_idx);
    } 
}

// 处理单字节的键盘输入号
static void do_normal_key(uint8_t raw_code)
{
    char key = get_key(raw_code);
    int is_make = is_make_code(raw_code);
    
    if(key == KEY_RSHIFT)
    {
        kdb_status.rshift_press = is_make ? 1 : 0;
    }
    else if(key == KEY_LSHIFT)
    {
        kdb_status.lshift_press = is_make ? 1 : 0;
    }
    else if(key == KEY_CAPS)
    {
        if(is_make) kdb_status.caps = ~kdb_status.caps;
    }
    else if(key == KEY_ALT)
    {
        kdb_status.lalt_press = is_make ? 1 : 0;
    }
    else if(key == KEY_CTRL)
    {
        kdb_status.lctrl_press = is_make ? 1 : 0;
    }
    else if(key>=KEY_F1 && key<=KEY_F8)
    {
        do_fx_key(key);
    }

    else 
    {
        if(is_make)
        {
            if(kdb_status.rshift_press || kdb_status.lshift_press)
            {
                key = key_map_table[key].func;
            }
            else
            {
                key = key_map_table[key].normal;
            }

            if(kdb_status.caps)
            {
                if(key >= 'A' && key <= 'Z')
                {
                    key = key - 'A' + 'a';
                }
                if(key >= 'a' && key <= 'z')
                {
                    key = key - 'a' + 'A';
                } 
            }
            tty_in(key);
            // log_printf("key: %c\n", key);
        }
    }
}

static void do_e0_key(uint8_t raw_code)
{
    char key = get_key(raw_code);
    int is_make = is_make_code(raw_code);
    
    if(key == KEY_ALT)
    {
        kdb_status.ralt_press = is_make ? 1 : 0;
    }
    else if(key == KEY_CTRL)
    {
        kdb_status.rctrl_press = is_make ? 1 : 0;
    }
}

void do_handler_kbd(exception_frame_t * exception_frame)
{
    static enum
    {
        NORMAL, // 单字节键盘输入
        BEGIN_E0,   // E0开头 2字节的输入
        BEGIN_E1,   // E1开头 6字节的输入
    }RECV_status = NORMAL;
    uint32_t status = inb(KBD_PORT_SATA);
    if(!(status & KBD_status_RECV_READY))
    {
        irq_send_eoi(IRQ0X21_KEYBOARD);
        return;
    }

    // 读取键值
    uint8_t raw_code = inb(KBD_PORT_DATA);

	// 读取完成之后，就可以发EOI，方便后续继续响应键盘中断
	// 否则,键值的处理过程可能略长，将导致中断响应延迟
    irq_send_eoi(IRQ0X21_KEYBOARD);

    // 实测qemu下收不到E0和E1，估计是没有发出去
    // 方向键、HOME/END等键码和小键盘上发出来的完全一样。不清楚原因
    // 也许是键盘布局的问题？所以，这里就忽略小键盘？
    if(raw_code == KEY_E0)
    {
        RECV_status = BEGIN_E0;
    } 
    else if(raw_code == KEY_E1)
    {
        RECV_status = BEGIN_E1;
    }
    else 
    {
        if(RECV_status == NORMAL)
        {
            do_normal_key(raw_code);
        }
        else if(RECV_status == KEY_E0)
        {
            do_e0_key(raw_code);    
            RECV_status = NORMAL;
        } 
        else if(RECV_status == KEY_E1)
        {
            // //暂不做处理
            // do_e1_key(raw_code);    
            RECV_status = NORMAL;
        } 
    }
}

void kbd_init(void)
{
    // tty设备启用时，才启用键盘
    // 但存在多个tty设备，只需要初始化一次
    static int inited = 0;
    if(!inited)
    {
        inited = 1;
        // kernel_memset(&kdb_status, 0, sizeof(kdb_status));
        irq_install(IRQ0X21_KEYBOARD,(uint32_t)exception_handler_kbd);
        irq_enable(IRQ0X21_KEYBOARD);
    }
}